package com.chebaohui.iqs.service.impl.deal.union.sdk;


import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;

import com.chebaohui.iqs.service.impl.deal.union.entity.Union;



public class UnionSign {
	/**
	 * 多证书签名(通过传入私钥证书路径和密码签名）<br>
	 * 功能：如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置
	 * acpsdk.singleMode=false)<br>
	 * 
	 * @param reqData
	 *            请求报文map<br>
	 * @param certPath
	 *            签名私钥文件（带路径）<br>
	 * @param certPwd
	 *            签名私钥密码<br>
	 * @param encoding
	 *            上送请求报文域encoding字段的值<br>
	 * @return 签名后的map对象<br>
	 */
	public static Map<String, String> signByCertInfo(Map<String, String> reqData, String certPath, String certPwd,
			String encoding, Union union) {
		reqData = filterBlank(reqData);
		UnionCert cert = new UnionCert(union);
		signByCertInfo(reqData, certPath, certPwd, encoding, cert);
		return reqData;
	}

	/**
	 * 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
	 * 功能：对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
	 * 
	 * @param reqData
	 *            请求报文map<br>
	 * @param encoding
	 *            上送请求报文域encoding字段的值<br>
	 * @return 签名后的map对象<br>
	 */
	public static Map<String, String> sign(Map<String, String> reqData, String encoding, Union union) {
		reqData = filterBlank(reqData);
		UnionCert cert = new UnionCert(union);
		toSign(reqData, encoding, cert);
		return reqData;
	}

	/**
	 * 过滤请求报文中的空字符串或者空字符串
	 * 
	 * @param contentData
	 * @return
	 */
	public static Map<String, String> filterBlank(Map<String, String> contentData) {
		LogUtil.writeLog("打印请求报文域 :");
		Map<String, String> submitFromData = new HashMap<String, String>();
		Set<String> keyset = contentData.keySet();

		for (String key : keyset) {
			String value = contentData.get(key);
			if (StringUtils.isNotBlank(value)) {
				// 对value值进行去除前后空处理
				submitFromData.put(key, value.trim());
				LogUtil.writeLog(key + "-->" + String.valueOf(value));
			}
		}
		return submitFromData;
	}

	/**
	 * 根据signMethod的值，提供三种计算签名的方法
	 * 
	 * @param data
	 *            待签名数据Map键值对形式
	 * @param encoding
	 *            编码
	 * @return 签名是否成功
	 */
	public static boolean toSign(Map<String, String> data, String encoding, UnionCert cert) {

		if (isEmpty(encoding)) {
			encoding = "UTF-8";
		}
		String signMethod = data.get(ProKey.param_signMethod);
		String version = data.get(ProKey.param_version);
		if (!ProKey.VERSION_1_0_0.equals(version) && !ProKey.VERSION_5_0_1.equals(version) && isEmpty(signMethod)) {
			LogUtil.writeErrorLog("signMethod must Not null");
			return false;
		}

		if (isEmpty(version)) {
			LogUtil.writeErrorLog("version must Not null");
			return false;
		}
		if (ProKey.SIGNMETHOD_RSA.equals(signMethod) || ProKey.VERSION_1_0_0.equals(version)
				|| ProKey.VERSION_5_0_1.equals(version)) {
			if (ProKey.VERSION_5_0_0.equals(version) || ProKey.VERSION_1_0_0.equals(version)
					|| ProKey.VERSION_5_0_1.equals(version)) {
				// 设置签名证书序列号
				data.put(ProKey.param_certId, cert.getSignCertId());
				// 将Map信息转换成key1=value1&key2=value2的形式
				String stringData = coverMap2String(data);
				LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");
				byte[] byteSign = null;
				String stringSign = null;
				try {
					// 通过SHA1进行摘要并转16进制
					byte[] signDigest = SecureUtil.sha1X16(stringData, encoding);
					byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft(cert.getSignCertPrivateKey(), signDigest));
					stringSign = new String(byteSign);
					// 设置签名域值
					data.put(ProKey.param_signature, stringSign);
					return true;
				} catch (Exception e) {
					LogUtil.writeErrorLog("Sign Error", e);
					return false;
				}
			} else if (ProKey.VERSION_5_1_0.equals(version)) {
				// 设置签名证书序列号
				data.put(ProKey.param_certId, cert.getSignCertId());
				// 将Map信息转换成key1=value1&key2=value2的形式
				String stringData = coverMap2String(data);
				LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");
				byte[] byteSign = null;
				String stringSign = null;
				try {
					// 通过SHA256进行摘要并转16进制
					byte[] signDigest = SecureUtil.sha256X16(stringData, encoding);
					byteSign = SecureUtil
							.base64Encode(SecureUtil.signBySoft256(cert.getSignCertPrivateKey(), signDigest));
					stringSign = new String(byteSign);
					// 设置签名域值
					data.put(ProKey.param_signature, stringSign);
					return true;
				} catch (Exception e) {
					LogUtil.writeErrorLog("Sign Error", e);
					return false;
				}
			}
		} else if (ProKey.SIGNMETHOD_SHA256.equals(signMethod)) {
			return signBySecureKey(data, UnionConfig.secureKey, encoding);
		} else if (ProKey.SIGNMETHOD_SM3.equals(signMethod)) {
			return signBySecureKey(data, UnionConfig.secureKey, encoding);
		}
		return false;
	}

	/**
	 * 通过传入的签名密钥进行签名并返回签名值<br>
	 * 
	 * @param data
	 *            待签名数据Map键值对形式
	 * @param encoding
	 *            编码
	 * @param certPath
	 *            证书绝对路径
	 * @param certPwd
	 *            证书密码
	 * @return 签名值
	 */
	public static boolean signByCertInfo(Map<String, String> data, String certPath, String certPwd, String encoding,
			UnionCert cert) {

		if (isEmpty(encoding)) {
			encoding = "UTF-8";
		}
		if (isEmpty(certPath) || isEmpty(certPwd)) {
			LogUtil.writeErrorLog("CertPath or CertPwd is empty");
			return false;
		}
		String signMethod = data.get(ProKey.param_signMethod);
		String version = data.get(ProKey.param_version);
		if (!ProKey.VERSION_1_0_0.equals(version) && !ProKey.VERSION_5_0_1.equals(version) && isEmpty(signMethod)) {
			LogUtil.writeErrorLog("signMethod must Not null");
			return false;
		}
		if (isEmpty(version)) {
			LogUtil.writeErrorLog("version must Not null");
			return false;
		}

		if (ProKey.SIGNMETHOD_RSA.equals(signMethod) || ProKey.VERSION_1_0_0.equals(version)
				|| ProKey.VERSION_5_0_1.equals(version)) {
			if (ProKey.VERSION_5_0_0.equals(version) || ProKey.VERSION_1_0_0.equals(version)
					|| ProKey.VERSION_5_0_1.equals(version)) {
				// 设置签名证书序列号
				data.put(ProKey.param_certId, cert.getCertIdByKeyStoreMap(certPath, certPwd));
				// 将Map信息转换成key1=value1&key2=value2的形式
				String stringData = coverMap2String(data);
				LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");
				byte[] byteSign = null;
				String stringSign = null;
				try {
					// 通过SHA1进行摘要并转16进制
					byte[] signDigest = SecureUtil.sha1X16(stringData, encoding);
					byteSign = SecureUtil.base64Encode(
							SecureUtil.signBySoft(cert.getSignCertPrivateKeyByStoreMap(certPath, certPwd), signDigest));
					stringSign = new String(byteSign);
					// 设置签名域值
					data.put(ProKey.param_signature, stringSign);
					return true;
				} catch (Exception e) {
					LogUtil.writeErrorLog("Sign Error", e);
					return false;
				}
			} else if (ProKey.VERSION_5_1_0.equals(version)) {
				// 设置签名证书序列号
				data.put(ProKey.param_certId, cert.getCertIdByKeyStoreMap(certPath, certPwd));
				// 将Map信息转换成key1=value1&key2=value2的形式
				String stringData = coverMap2String(data);
				LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");
				byte[] byteSign = null;
				String stringSign = null;
				try {
					// 通过SHA256进行摘要并转16进制
					byte[] signDigest = SecureUtil.sha256X16(stringData, encoding);
					byteSign = SecureUtil.base64Encode(SecureUtil
							.signBySoft256(cert.getSignCertPrivateKeyByStoreMap(certPath, certPwd), signDigest));
					stringSign = new String(byteSign);
					// 设置签名域值
					data.put(ProKey.param_signature, stringSign);
					return true;
				} catch (Exception e) {
					LogUtil.writeErrorLog("Sign Error", e);
					return false;
				}
			}

		}
		return false;
	}

	/**
	 * 通过传入的证书绝对路径和证书密码读取签名证书进行签名并返回签名值<br>
	 * 
	 * @param data
	 *            待签名数据Map键值对形式
	 * @param encoding
	 *            编码
	 * @param certPath
	 *            证书绝对路径
	 * @param certPwd
	 *            证书密码
	 * @return 签名值
	 */
	public static boolean signBySecureKey(Map<String, String> data, String secureKey, String encoding) {

		if (isEmpty(encoding)) {
			encoding = "UTF-8";
		}
		if (isEmpty(secureKey)) {
			LogUtil.writeErrorLog("secureKey is empty");
			return false;
		}
		String signMethod = data.get(ProKey.param_signMethod);
		if (isEmpty(signMethod)) {
			LogUtil.writeErrorLog("signMethod must Not null");
			return false;
		}

		if (ProKey.SIGNMETHOD_SHA256.equals(signMethod)) {
			// 将Map信息转换成key1=value1&key2=value2的形式
			String stringData = coverMap2String(data);
			LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");
			String strBeforeSha256 = stringData + ProKey.AMPERSAND + SecureUtil.sha256X16Str(secureKey, encoding);
			String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256, encoding);
			// 设置签名域值
			data.put(ProKey.param_signature, strAfterSha256);
			return true;
		} else if (ProKey.SIGNMETHOD_SM3.equals(signMethod)) {
			String stringData = coverMap2String(data);
			LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");
			String strBeforeSM3 = stringData + ProKey.AMPERSAND + SecureUtil.sm3X16Str(secureKey, encoding);
			String strAfterSM3 = SecureUtil.sm3X16Str(strBeforeSM3, encoding);
			// 设置签名域值
			data.put(ProKey.param_signature, strAfterSM3);
			return true;
		}
		return false;
	}

	/**
	 * 将Map中的数据转换成key1=value1&key2=value2的形式 不包含签名域signature
	 * 
	 * @param data
	 *            待拼接的Map数据
	 * @return 拼接好后的字符串
	 */
	public static String coverMap2String(Map<String, String> data) {
		TreeMap<String, String> tree = new TreeMap<String, String>();
		Iterator<Entry<String, String>> it = data.entrySet().iterator();
		while (it.hasNext()) {
			Entry<String, String> en = it.next();
			if (ProKey.param_signature.equals(en.getKey().trim())) {
				continue;
			}
			tree.put(en.getKey(), en.getValue());
		}
		it = tree.entrySet().iterator();
		StringBuffer sf = new StringBuffer();
		while (it.hasNext()) {
			Entry<String, String> en = it.next();
			sf.append(en.getKey() + ProKey.EQUAL + en.getValue() + ProKey.AMPERSAND);
		}
		return sf.substring(0, sf.length() - 1);
	}

	/**
	 * 判断字符串是否为NULL或空
	 * 
	 * @param s
	 *            待判断的字符串数据
	 * @return 判断结果 true-是 false-否
	 */
	public static boolean isEmpty(String s) {
		return null == s || "".equals(s.trim());
	}
}

